﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Threading;
using System.Net.Sockets;
using System.Runtime.InteropServices;

using System.IO;

namespace RegPort
{
    public partial class PortPanel : Form
    {
        private const int WM_NCHITTEST = 0x84;
        private const int HTCLIENT = 0x1;
        private const int HTCAPTION = 0x2;
        private const int ENDPORT = 65535;
        private const int BEGINPORT = 0;

        private List<String> ipList = new List<String>();

        
        private int m_port_start = 0;
        private int m_port_end = 65535;
        private int m_threadCount = 100;

        private String m_bulk_ips = "";

        public int StartPortRange { get; set; }
        public int EndPortRange { get; set; }
        private List<String> ipaddresses = new List<String>();
        private String m_active_ip = "";

        private int m_active_port = 0;


        public PortPanel()
        {
            InitializeComponent();
        }

        ///
        /// Handling the window messages
        ///
        protected override void WndProc(ref Message message)
        {
            base.WndProc(ref message);

            if (message.Msg == WM_NCHITTEST && (int)message.Result == HTCLIENT)
                message.Result = (IntPtr)HTCAPTION;
        }



        private void btnCloseForm_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void PortPanel_Load(object sender, EventArgs e)
        {
            this.Width = 350;
            this.txtIPAddress.Focus();
            txtPortStart.Text = BEGINPORT.ToString();
            txtPortEnd.Text = ENDPORT.ToString();
        }

        private void pScanResults_Paint(object sender, PaintEventArgs e)
        {
            ControlPaint.DrawBorder(e.Graphics, this.pScanResults.DisplayRectangle, System.Drawing.Color.White, ButtonBorderStyle.Solid);
        }

        private void lnkAdvanced_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (this.Width == 350) this.Width = 709;
            else this.Width = 350;
        }


        private List<String> BuildIPList(String list, String src)
        {
            List<String> ips = new List<String>();
            if(list.Length > 5)
            {
                String[] split;
                if (src == "b")
                {
                    string[] stringSeparators = new string[] { "\r\n" };
                    split = list.Split(stringSeparators,StringSplitOptions.None);
                }
                else split = list.Split(';');
                foreach (String ip in split)
                {
                    ips.Add(ip);
                }
            }
            return ips;
        }

        /// <summary>
        /// Determine if user wants to start Scan - then check if not already running!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnScan_Click(object sender, EventArgs e)
        {
            if ((txtIPAddress.TextLength > 6) || (m_bulk_ips.Length > 6))
            {
                DialogResult dr = MessageBox.Show("Do you wish to scan?", "Start Scan", MessageBoxButtons.YesNo);
                if (dr == DialogResult.Yes)
                {
                    this.Cursor = Cursors.AppStarting;
                    if (txtPortStart.Text == "") m_port_start = BEGINPORT;
                    else m_port_start = int.Parse(txtPortStart.Text);
                    if (txtPortEnd.Text == "") m_port_start = ENDPORT;
                    else m_port_end = int.Parse(txtPortEnd.Text);

                    if (txtIPAddress.Enabled) ipList = BuildIPList(txtIPAddress.Text,"");
                    else ipList = BuildIPList(m_bulk_ips, "b");
                    if (!IsScanCurrentlyRunning)
                    {
                        
                        IsScanCurrentlyRunning = true;
                        StartPortRange = m_port_start;
                        EndPortRange = m_port_end;
                        Start(ipList);
                        
                        IsScanCurrentlyRunning = false; //reset
                    };
                    this.Cursor = Cursors.Default;
                };

                txtIPAddress.Enabled = true;
                txtIPAddress.Focus();
                ipList = null;
            }
            else MessageBox.Show("You must provide a DNS/IP Address to scan");
        }

        private Boolean IsScanCurrentlyRunning
        {
            get; set;
        }



        private void txtPortStart_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!Char.IsDigit(e.KeyChar)) 
            {
                e.Handled = true;
            }
        }

        private void txtPortEnd_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!Char.IsDigit(e.KeyChar))
            {
                e.Handled = true;
            }
        }



        #region Scanning
        private void Start(List<String> ipToScan)
        {
            this.ipaddresses = ipToScan;
            foreach(String ip in ipaddresses)
            {
                this.m_active_ip = ip;
                ScanningPort();
            }
        }

        private void ScanningPort()
        {
            Thread[] threadScan = new Thread[100];
            m_active_port = StartPortRange;
            for (int pntr = 0; pntr <= (threadScan.Length - 1); pntr++)
            {
                threadScan[pntr] = new Thread(Scan);
                threadScan[pntr].Name = "RegScanner" + pntr;
                threadScan[pntr].IsBackground = true;
                threadScan[pntr].Start();
            }
            Thread.Sleep(200);
        }


        private void Scan()
        {
            TcpClient tcpSock;
            int asyncPort;
            do
            {
                m_active_port++;
                asyncPort = m_active_port;
                try
                {
                    tcpSock = new TcpClient();
                    tcpSock.Connect(m_active_ip, asyncPort);
                    System.Threading.Thread.Sleep(150); //TODO: Make dynamic
                    if (tcpSock.Connected)
                    {
                        DisplayInList(m_active_ip + ";" + asyncPort + ";Open");
                        tcpSock.Close(); 
                    }
                    //else DisplayInList(m_active_ip + ";" + asyncPort + ";Closed");
                }
                catch (Exception ex)
                {
                    //DisplayInList(m_active_ip + ";" + asyncPort + ";Closed");
                }
            } while (this.m_active_port < this.EndPortRange);

            m_threadCount--; //reduce the thread count
        }

        #endregion

        #region GUI Display Functions
        delegate void lstCallbackASYNC(String lstText);
        private void DisplayInList(String lstItem)
        {
            System.Threading.Thread.Sleep(100);
            if (this.lstResults.InvokeRequired)
            {
                lstCallbackASYNC d = new lstCallbackASYNC(FormatListItem);
                this.Invoke(d, new object[] { lstItem });
            }
            else FormatListItem(lstItem);
        }

        private void FormatListItem(String s)
        {
            String[] lItem = s.Split(';'); 
            this.lstResults.BeginUpdate();

            ListViewItem l = new ListViewItem();
            l.Text = lItem[0];

            ListViewItem.ListViewSubItem ls;
            
            ls= new ListViewItem.ListViewSubItem(); ;
            ls.Text = lItem[1];
            l.SubItems.Add(ls);

            ls= new ListViewItem.ListViewSubItem();
            ls.Text = lItem[2];
            l.SubItems.Add(ls);

            this.lstResults.Items.Add(l);
            this.lstResults.EndUpdate();


        }

        #endregion

        private void btImportIpList_Click(object sender, EventArgs e)
        {
            Stream m_stream = null;
            opnIPListFile.Filter = "Text Files (.txt)|*.txt";
            opnIPListFile.FilterIndex = 1;

            opnIPListFile.Multiselect = false;
            DialogResult dr = opnIPListFile.ShowDialog();

            String fname = "";
            if (dr == DialogResult.OK)
            {

                fname = opnIPListFile.FileName;
                try
                {
                    if ((m_stream = opnIPListFile.OpenFile()) != null)
                    {
                        using (System.IO.StreamReader reader = new System.IO.StreamReader(m_stream, Encoding.UTF8))
                        {
                            if (this.m_bulk_ips.Length == 0) this.m_bulk_ips = reader.ReadToEnd();
                            else this.m_bulk_ips = m_bulk_ips + reader.ReadToEnd();
                        }
                        m_stream.Close();
                        if (m_bulk_ips.Length > 5)
                        {
                            txtIPAddress.Enabled = false;
                            txtIPAddress.Text = "";
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }

        }

    }//--eof
}
